home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr26 / netprog.zip / NETPROG.TAR / lib.s5 / semaph.c < prev    next >
C/C++ Source or Header  |  1989-12-17  |  8KB  |  290 lines

  1. /*
  2.  * Provide an simpler and easier to understand interface to the System V
  3.  * semaphore system calls.  There are 7 routines available to the user:
  4.  *
  5.  *    id = sem_create(key, initval);    # create with initial value or open
  6.  *    id = sem_open(key);        # open (must already exist)
  7.  *    sem_wait(id);            # wait = P = down by 1
  8.  *    sem_signal(id);            # signal = V = up by 1
  9.  *    sem_op(id, amount);        # wait   if (amount < 0)
  10.  *                    # signal if (amount > 0)
  11.  *    sem_close(id);            # close
  12.  *    sem_rm(id);            # remove (delete)
  13.  *
  14.  * We create and use a 3-member set for the requested semaphore.
  15.  * The first member, [0], is the actual semaphore value, and the second
  16.  * member, [1], is a counter used to know when all processes have finished
  17.  * with the semaphore.  The counter is initialized to a large number,
  18.  * decremented on every create or open and incremented on every close.
  19.  * This way we can use the "adjust" feature provided by System V so that
  20.  * any process that exit's without calling sem_close() is accounted
  21.  * for.  It doesn't help us if the last process does this (as we have
  22.  * no way of getting control to remove the semaphore) but it will
  23.  * work if any process other than the last does an exit (intentional
  24.  * or unintentional).
  25.  * The third member, [2], of the semaphore set is used as a lock variable
  26.  * to avoid any race conditions in the sem_create() and sem_close()
  27.  * functions.
  28.  */
  29.  
  30. #include    <sys/types.h>
  31. #include    <sys/ipc.h>
  32. #include    <sys/sem.h>
  33.  
  34. #include    <errno.h>
  35. extern int    errno;
  36.  
  37. #define    BIGCOUNT    10000        /* initial value of process counter */
  38.  
  39. /*
  40.  * Define the semaphore operation arrays for the semop() calls.
  41.  */
  42.  
  43. static struct sembuf    op_lock[2] = {
  44.     2, 0, 0,    /* wait for [2] (lock) to equal 0 */
  45.     2, 1, SEM_UNDO    /* then increment [2] to 1 - this locks it */
  46.             /* UNDO to release the lock if processes exits
  47.                before explicitly unlocking */
  48. };
  49.  
  50. static struct sembuf    op_endcreate[2] = {
  51.     1, -1, SEM_UNDO,/* decrement [1] (proc counter) with undo on exit */
  52.             /* UNDO to adjust proc counter if process exits
  53.                before explicitly calling sem_close() */
  54.     2, -1, SEM_UNDO    /* then decrement [2] (lock) back to 0 */
  55. };
  56.  
  57. static struct sembuf    op_open[1] = {
  58.     1, -1, SEM_UNDO    /* decrement [1] (proc counter) with undo on exit */
  59. };
  60.  
  61. static struct sembuf    op_close[3] = {
  62.     2, 0, 0,    /* wait for [2] (lock) to equal 0 */
  63.     2, 1, SEM_UNDO,    /* then increment [2] to 1 - this locks it */
  64.     1, 1, SEM_UNDO    /* then increment [1] (proc counter) */
  65. };
  66.  
  67. static struct sembuf    op_unlock[1] = {
  68.     2, -1, SEM_UNDO    /* decrement [2] (lock) back to 0 */
  69. };
  70.  
  71. static struct sembuf    op_op[1] = {
  72.     0, 99, SEM_UNDO    /* decrement or increment [0] with undo on exit */
  73.             /* the 99 is set to the actual amount to add
  74.                or subtract (positive or negative) */
  75. };
  76.  
  77. /****************************************************************************
  78.  * Create a semaphore with a specified initial value.
  79.  * If the semaphore already exists, we don't initialize it (of course).
  80.  * We return the semaphore ID if all OK, else -1.
  81.  */
  82.  
  83. int
  84. sem_create(key, initval)
  85. key_t    key;
  86. int    initval;    /* used if we create the semaphore */
  87. {
  88.     register int        id, semval;
  89.     union semun {
  90.         int        val;
  91.         struct semid_ds    *buf;
  92.         ushort        *array;
  93.     } semctl_arg;
  94.  
  95.     if (key == IPC_PRIVATE)
  96.         return(-1);    /* not intended for private semaphores */
  97.  
  98.     else if (key == (key_t) -1)
  99.         return(-1);    /* probably an ftok() error by caller */
  100.  
  101. again:
  102.     if ( (id = semget(key, 3, 0666 | IPC_CREAT)) < 0)
  103.         return(-1);    /* permission problem or tables full */
  104.  
  105.     /*
  106.      * When the semaphore is created, we know that the value of all
  107.      * 3 members is 0.
  108.      * Get a lock on the semaphore by waiting for [2] to equal 0,
  109.      * then increment it.
  110.      *
  111.      * There is a race condition here.  There is a possibility that
  112.      * between the semget() above and the semop() below, another
  113.      * process can call our sem_close() function which can remove
  114.      * the semaphore if that process is the last one using it.
  115.      * Therefore, we handle the error condition of an invalid
  116.      * semaphore ID specially below, and if it does happen, we just
  117.      * go back and create it again.
  118.      */
  119.  
  120.     if (semop(id, &op_lock[0], 2) < 0) {
  121.         if (errno == EINVAL)
  122.             goto again;
  123.         err_sys("can't lock");
  124.     }
  125.  
  126.     /*
  127.      * Get the value of the process counter.  If it equals 0,
  128.      * then no one has initialized the semaphore yet.
  129.      */
  130.  
  131.     if ( (semval = semctl(id, 1, GETVAL, 0)) < 0)
  132.         err_sys("can't GETVAL");
  133.  
  134.     if (semval == 0) {
  135.         /*
  136.          * We could initialize by doing a SETALL, but that
  137.          * would clear the adjust value that we set when we
  138.          * locked the semaphore above.  Instead, we'll do 2
  139.          * system calls to initialize [0] and [1].
  140.          */
  141.  
  142.         semctl_arg.val = initval;
  143.         if (semctl(id, 0, SETVAL, semctl_arg) < 0)
  144.             err_sys("can SETVAL[0]");
  145.  
  146.         semctl_arg.val = BIGCOUNT;
  147.         if (semctl(id, 1, SETVAL, semctl_arg) < 0)
  148.             err_sys("can SETVAL[1]");
  149.     }
  150.  
  151.     /*
  152.      * Decrement the process counter and then release the lock.
  153.      */
  154.  
  155.     if (semop(id, &op_endcreate[0], 2) < 0)
  156.         err_sys("can't end create");
  157.  
  158.     return(id);
  159. }
  160.  
  161. /****************************************************************************
  162.  * Open a semaphore that must already exist.
  163.  * This function should be used, instead of sem_create(), if the caller
  164.  * knows that the semaphore must already exist.  For example a client
  165.  * from a client-server pair would use this, if its the server's
  166.  * responsibility to create the semaphore.
  167.  * We return the semaphore ID if all OK, else -1.
  168.  */
  169.  
  170. int
  171. sem_open(key)
  172. key_t    key;
  173. {
  174.     register int    id;
  175.  
  176.     if (key == IPC_PRIVATE)
  177.         return(-1);    /* not intended for private semaphores */
  178.  
  179.     else if (key == (key_t) -1)
  180.         return(-1);    /* probably an ftok() error by caller */
  181.  
  182.     if ( (id = semget(key, 3, 0)) < 0)
  183.         return(-1);    /* doesn't exist, or tables full */
  184.  
  185.     /*
  186.      * Decrement the process counter.  We don't need a lock
  187.      * to do this.
  188.      */
  189.  
  190.     if (semop(id, &op_open[0], 1) < 0)
  191.         err_sys("can't open");
  192.  
  193.     return(id);
  194. }
  195.  
  196. /****************************************************************************
  197.  * Remove a semaphore.
  198.  * This call is intended to be called by a server, for example,
  199.  * when it is being shut down, as we do an IPC_RMID on the semaphore,
  200.  * regardless whether other processes may be using it or not.
  201.  * Most other processes should use sem_close() below.
  202.  */
  203.  
  204. sem_rm(id)
  205. int    id;
  206. {
  207.     if (semctl(id, 0, IPC_RMID, 0) < 0)
  208.         err_sys("can't IPC_RMID");
  209. }
  210.  
  211. /****************************************************************************
  212.  * Close a semaphore.
  213.  * Unlike the remove function above, this function is for a process
  214.  * to call before it exits, when it is done with the semaphore.
  215.  * We "decrement" the counter of processes using the semaphore, and
  216.  * if this was the last one, we can remove the semaphore.
  217.  */
  218.  
  219. sem_close(id)
  220. int    id;
  221. {
  222.     register int    semval;
  223.  
  224.     /*
  225.      * The following semop() first gets a lock on the semaphore,
  226.      * then increments [1] - the process counter.
  227.      */
  228.  
  229.     if (semop(id, &op_close[0], 3) < 0)
  230.         err_sys("can't semop");
  231.  
  232.     /*
  233.      * Now that we have a lock, read the value of the process
  234.      * counter to see if this is the last reference to the
  235.      * semaphore.
  236.      * There is a race condition here - see the comments in
  237.      * sem_create().
  238.      */
  239.  
  240.     if ( (semval = semctl(id, 1, GETVAL, 0)) < 0)
  241.         err_sys("can't GETVAL");
  242.  
  243.     if (semval > BIGCOUNT)
  244.         err_dump("sem[1] > BIGCOUNT");
  245.     else if (semval == BIGCOUNT)
  246.         sem_rm(id);
  247.     else
  248.         if (semop(id, &op_unlock[0], 1) < 0)
  249.             err_sys("can't unlock");    /* unlock */
  250. }
  251.  
  252. /****************************************************************************
  253.  * Wait until a semaphore's value is greater than 0, then decrement
  254.  * it by 1 and return.
  255.  * Dijkstra's P operation.  Tanenbaum's DOWN operation.
  256.  */
  257.  
  258. sem_wait(id)
  259. int    id;
  260. {
  261.     sem_op(id, -1);
  262. }
  263.  
  264. /****************************************************************************
  265.  * Increment a semaphore by 1.
  266.  * Dijkstra's V operation.  Tanenbaum's UP operation.
  267.  */
  268.  
  269. sem_signal(id)
  270. int    id;
  271. {
  272.     sem_op(id, 1);
  273. }
  274.  
  275. /****************************************************************************
  276.  * General semaphore operation.  Increment or decrement by a user-specified
  277.  * amount (positive or negative; amount can't be zero).
  278.  */
  279.  
  280. sem_op(id, value)
  281. int    id;
  282. int    value;
  283. {
  284.     if ( (op_op[0].sem_op = value) == 0)
  285.         err_sys("can't have value == 0");
  286.  
  287.     if (semop(id, &op_op[0], 1) < 0)
  288.         err_sys("sem_op error");
  289. }
  290.